Skip to content

Hello React

Let's start with a “hello world” React application, using pure JavaScript:

Code Playground

Open in CodeSandbox
// 1. Import dependencies
import React from 'react';
import { createRoot } from 'react-dom/client';

// 2. Create a React element
const element = React.createElement(
'p',
{ id: 'hello' },
'Hello World!'
);

// 3. Render the application
const container = document.querySelector('#root');
const root = createRoot(container);
root.render(element);

We start with two files: an index.html file that includes a barebones HTML document, and an index.js file with a minimal React application.

When we run this code, we're left with a paragraph that displays the text “Hello World!”.

There's a lot to unpack here. We'll go through it one section at a time. If you're up for it, though, I'd encourage you to spend a couple minutes poking and prodding at this example. See what you can figure out with a bit of !

1. Import dependencies

import React from 'react';
import { createRoot } from 'react-dom/client';

At the very top of the file, we have two import statements, using the native JavaScript module system. We're importing the core React library from the react dependency, as well as a createRoot function from react-dom.

If you're wondering why there are two separate packages, this is because React itself is “platform agnostic”. We have the core react package, and then there are different platform-specific renderers:

  • react-dom for the web
  • react-native for mobile (iOS / Android) or desktop (Windows / macOS) applications
  • react-three-fiber for 3D scenes using WebGL and Three.js

Every platform has its own “primitives”, the set of built-in elements we use to create our UI. On the web, the primitives are HTML elements like <div> and <p> and <button>. By contrast, React Native doesn't have divs, it has Text and View and Pressable. And things get even more wild with react-three-fiber, where the primitives are things like lights, geometries, materials, and cameras.

All of these platforms will use the same core React framework, which comes from the react package. But when it comes to actually turning all of the business logic into a user interface, we need the correct bindings for our platform.

This is actually a terrific thing, because it means that the skills you build learning React can also be used to build mobile applications or 3D interfaces, if that's where your interests or your career takes you!

2. Create a React element

Next up in our mini-application, we have the following code:

const element = React.createElement(
'p',
{ id: 'hello' },
'Hello World!'
);

React.createElement is a function that accepts 3 or more arguments:

  1. The type of the element to create.
  2. The properties we want this element to have.
  3. The element's contents, what the element should have as children.

This function returns a “React element”. React elements are plain old JavaScript objects. If we inspect it with console.log(element), we'll see something like this:

{
type: "p",
key: null,
ref: null,
props: {
id: 'hello',
children: 'Hello World!',
},
_owner: null,
_store: { validated: false }
}

This JavaScript object is a description of a hypothetical paragraph tag, with an ID of hello, containing the text "Hello World!". This information will be used to construct the actual paragraph we can see in-browser.

Later in this course, we'll learn about key and ref. The final two properties, _owner and _store, are meant to be used internally by React, and can be safely ignored by us.

3. Render the application

We've made it to the last few lines of code:

const container = document.querySelector('#root');
const root = createRoot(container);
root.render(element);

document.querySelector is a helpful function that lets us capture a reference to a pre-existing DOM element. It's the modern version of document.getElementById, if you're more familiar with that function.

It works in this case because our index.html file includes the following element:

<div id="root"></div>

This element will be our application's container. When we render our React application, it will create and append new DOM element(s) to this container.

With react-dom's createRoot function, we specify that this element should be the root of our application. And, finally, we render the application with root.render(element).

You can think of the render function as a machine that converts React elements into DOM nodes. In this case, our React element describes a paragraph tag, with an ID, and some text inside. render will turn that description into the following DOM structure:

<p id="hello">
Hello World!
</p>

With that DOM element created, it then adds it to the page at the specified root. In essence, this code takes a JavaScript-based description of some HTML, and uses it to produce real-world DOM nodes.

This probably seems like a very complicated way to create a paragraph! But, as we'll learn throughout this course, the real magic happens when things change. ✨

Build systems

Video Summary

There's something a bit curious about the playground we've been looking at, in this lesson: There's no script tag!

<html>
<body>
<div id="root"></div>
</body>
</html>

How exactly is our code running, when we don't have a <script src="/index.js"></script>?

The thing to know about React is that it's used within a build system. The files we work with are inputs to this system.

We can see this firsthand, by inspecting the HTML in the “Result” <iframe>:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sandbox - Josh’s Course Platform</title>
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="mask-icon" href="/csb-ios.svg" color="#fff">
<script src="/static/browserfs12/browserfs.min.js" type="text/javascript"></script>
<script>
(window.process = BrowserFS.BFSRequire('process')),
(window.Buffer = BrowserFS.BFSRequire('buffer').Buffer);
</script>
<link href="/static/js/babel.7.12.12.min.js" rel="prefetch" as="script">
<script charset="utf-8" src="/static/js/2.61b71a6f1.chunk.js"></script>
<script charset="utf-8" src="/static/js/6.147d49776.chunk.js"></script>
</head>
<body>
<div id="root">
<p id="hello">Hello World!</p>
</div>
</body>
</html>

The index.js file, along with the dependencies (React, React DOM), are bundled into cryptically-named files like 2.61b71a6f1.chunk.js, and a <script> tag is dynamically injected into the HTML.

I don't expect you to understand what all of this markup is, or why it exists. The important thing to know, for now, is that there's an active process that transforms the files we write.

If you haven't used other JS frameworks, this probably seems wildly overengineered to you. Why can't we add our code ourselves, using <script> tags, the way we do when working with jQuery and other libraries?

There are a few reasons for this:

  • As we'll learn, JSX needs to be compiled into JS
  • It enables quality-of-life improvements, like being able to use bleeding-edge JS features
  • It's necessary for good performance, since React apps often have hundreds or even thousands of JS files that need to be bundled together.

We'll learn more about build systems in the “Tools of the Trade” reference module.